define(["require", "exports"], function (require, exports) {
    /* --------------------------------------------------------------------------------------------
     * Copyright (c) Microsoft Corporation. All rights reserved.
     * Licensed under the MIT License. See License.txt in the project root for license information.
     * ------------------------------------------------------------------------------------------ */
    'use strict';
    Object.defineProperty(exports, "__esModule", { value: true });
    var Disposable;
    (function (Disposable) {
        function create(func) {
            return {
                dispose: func
            };
        }
        Disposable.create = create;
    })(Disposable = exports.Disposable || (exports.Disposable = {}));
    var Event;
    (function (Event) {
        const _disposable = { dispose() { } };
        Event.None = function () { return _disposable; };
    })(Event = exports.Event || (exports.Event = {}));
    class CallbackList {
        add(callback, context = null, bucket) {
            if (!this._callbacks) {
                this._callbacks = [];
                this._contexts = [];
            }
            this._callbacks.push(callback);
            this._contexts.push(context);
            if (Array.isArray(bucket)) {
                bucket.push({ dispose: () => this.remove(callback, context) });
            }
        }
        remove(callback, context = null) {
            if (!this._callbacks) {
                return;
            }
            var foundCallbackWithDifferentContext = false;
            for (var i = 0, len = this._callbacks.length; i < len; i++) {
                if (this._callbacks[i] === callback) {
                    if (this._contexts[i] === context) {
                        // callback & context match => remove it
                        this._callbacks.splice(i, 1);
                        this._contexts.splice(i, 1);
                        return;
                    }
                    else {
                        foundCallbackWithDifferentContext = true;
                    }
                }
            }
            if (foundCallbackWithDifferentContext) {
                throw new Error('When adding a listener with a context, you should remove it with the same context');
            }
        }
        invoke(...args) {
            if (!this._callbacks) {
                return [];
            }
            var ret = [], callbacks = this._callbacks.slice(0), contexts = this._contexts.slice(0);
            for (var i = 0, len = callbacks.length; i < len; i++) {
                try {
                    ret.push(callbacks[i].apply(contexts[i], args));
                }
                catch (e) {
                    console.error(e);
                }
            }
            return ret;
        }
        isEmpty() {
            return !this._callbacks || this._callbacks.length === 0;
        }
        dispose() {
            this._callbacks = undefined;
            this._contexts = undefined;
        }
    }
    class Emitter {
        constructor(_options) {
            this._options = _options;
        }
        /**
         * For the public to allow to subscribe
         * to events from this Emitter
         */
        get event() {
            if (!this._event) {
                this._event = (listener, thisArgs, disposables) => {
                    if (!this._callbacks) {
                        this._callbacks = new CallbackList();
                    }
                    if (this._options && this._options.onFirstListenerAdd && this._callbacks.isEmpty()) {
                        this._options.onFirstListenerAdd(this);
                    }
                    this._callbacks.add(listener, thisArgs);
                    let result;
                    result = {
                        dispose: () => {
                            this._callbacks.remove(listener, thisArgs);
                            result.dispose = Emitter._noop;
                            if (this._options && this._options.onLastListenerRemove && this._callbacks.isEmpty()) {
                                this._options.onLastListenerRemove(this);
                            }
                        }
                    };
                    if (Array.isArray(disposables)) {
                        disposables.push(result);
                    }
                    return result;
                };
            }
            return this._event;
        }
        /**
         * To be kept private to fire an event to
         * subscribers
         */
        fire(event) {
            if (this._callbacks) {
                this._callbacks.invoke.call(this._callbacks, event);
            }
        }
        dispose() {
            if (this._callbacks) {
                this._callbacks.dispose();
                this._callbacks = undefined;
            }
        }
    }
    Emitter._noop = function () { };
    exports.Emitter = Emitter;
});
